package routers

import (
	"WZ_law-api/cm"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/ethereum/go-ethereum/common"

	//"strconv"

	"github.com/ethereum/go-ethereum/log"
)

//VerifyHandler manager how to call contract from picoChain
type VerifyHandler struct {
	chain *cm.ClientManager
}

//API input data
type VerifyRequest struct {
	Way int `json:"way"` //verify way: 1-fileID,2-Sha256,3-sm3,4-all
	//Contractaddr string `json:"contract_addr,omitempty"`
	TxHash string `json:"tran_hash,omitempty"` //用于查询对应的区块 TODO：后面可以加上时间戳
	//下面对应四种查询方式
	FileID       string `json:"file_id,omitempty"`
	Sha256       string `json:"sha256,omitempty"`
	Sm3          string `json:"sm3,omitempty"`
	Md5          string `json:"md5,omitempty"`
	ChainId      string `json:"chain_id",omitempty`
	ContractAddr string `json:"contract_addr"`
}

//VerifyResponse return status about verify request
//API回应数据格式
type VerifyResponse struct {
	Code int           `json:"code"`
	Msg  string        `json:"msg"`
	Data []*VerifyData `json:"data"`
}

//具体回应的ouput data
type VerifyData struct {
	BlockNumber  string `json:"block_height"`
	BlockHash    string `json:"block_hash"`
	TxHash       string `json:"tran_hash"`
	Timestamp    string `json:"req_timestamp"`
	TrustStamp   string `json:"trust_timestamp"`
	FileID       string `json:"file_id"`
	Sha256       string `json:"sha256"`
	Sm3          string `json:"sm3"`
	Md5          string `json:"md5"`
	ContractAddr string `json:"contract_addr"`
	IpfsHash     string `json:"ipfs_hash"`
	//Contractaddr string `json:"contract_addr"`
}

func NewVerifyHandler(chain *cm.ClientManager) *VerifyHandler {
	return &VerifyHandler{chain: chain}
}

//String show the main function of chain hanlder
func (vh *VerifyHandler) String() string {
	return "verify data in blockchain"
}

func (vh *VerifyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {

		w.Header().Add("Content-Type", "application/json;charset=utf-8")

		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Println("verifyHandler", "err", err)
			w.Write(encRespon(newVerifyResopnse(ErrHTTPRequest)))
			return
		}
		defer r.Body.Close()

		//parse request
		//解析request json到VerifyRequest对对象中
		req := new(VerifyRequest)
		if err = json.Unmarshal(b, req); err != nil {
			fmt.Println("verifyHandler", "err", err)
			w.Write(encRespon(newVerifyResopnse(ErrHTTPRequest)))
			return
		}

		//check tran_hash
		if req.TxHash == "" {
			fmt.Println("empty tran_hash")
			w.Write(encRespon(newVerifyResopnse(ErrTxHash)))
			return
		}

		//get staus from fisco
		//如verify没有出错，rescode为0，返回对应的数据，如果出错则数据段为nil
		res := vh.handleVerifyReq(req)
		w.Write(encRespon(res))

	} else {
		fmt.Println("unknown request", "method", r.Method)
		w.Write(encRespon(newVerifyResopnse(ErrHTTPRequest)))
	}
}

//handleVerifyReq select a vaild way to handle request.
//		way: 1, index: "fileid";
//		way: 2, index: "filehash";
//		way: 3, index: "sm3";
//		way: 4, index: "fileid" | "filehash" | "sm3" | "md5"
//when way is other status, only answer where transaction storage
func (vh *VerifyHandler) handleVerifyReq(req *VerifyRequest) *VerifyResponse {
	//when we succeed,fill data in this response
	res := newVerifyResopnse(nil)
	resData := &VerifyData{} //返回的data对象

	//从传入的交易hash获得对应的receipt
	receipt, err := vh.chain.Client.GetTransactionReceipt(context.Background(), common.HexToHash(req.TxHash))
	if err != nil {
		fmt.Println("GetTransactionReceipt err:", err)
		return newVerifyResopnse(err)
	}


	//check validity in this request,if we need get file status
	//返回verify模式和对应的参数
	if idx, mode := findIndex(req); idx != "" {
		//find origin data
		//number, _ := strconv.ParseUint(resData.BlockNumber, 10, 64)
		//获得respond data
		fData, err := vh.chain.GetProofData(idx, mode)
		if err != nil {
			log.Warn("verify err", "idx", idx, "txhash", req.TxHash, "err", err)
			return newVerifyResopnse(err)
		}
		resData.BlockNumber = receipt.BlockNumber //获取收据中的区块高度
		resData.BlockHash = receipt.BlockHash     //获取区块高度的hash
		resData.TxHash = req.TxHash	
		resData.FileID = fData.FileId
		resData.Sha256 = fData.FileHash
		resData.Sm3 = fData.FileSm3
		resData.Md5 = fData.FileMd5
		resData.Timestamp = fData.TimeStamp //TODO:只有在通过byFileId时这个才有值，但是还没有看合约确定是否是add时候的时间戳，应该是吧
		resData.TrustStamp = Trust[req.FileID]
		resData.IpfsHash = Ipfs[req.FileID]
	}
	res.Data = append(res.Data, resData)

	return res
}

//checkReqWay select the method for reading contract
func findIndex(req *VerifyRequest) (string, int) {
	if req.FileID != "" && (req.Way == 1 || req.Way == 4) {
		return req.FileID, 1
	}
	if req.Sha256 != "" && (req.Way == 2 || req.Way == 4) {
		return req.Sha256, 2
	}
	if req.Sm3 != "" && (req.Way == 3 || req.Way == 4) {
		return req.Sm3, 3
	}
	if req.Md5 != "" && (req.Way == 4) {
		return req.Md5, 4
	}
	return "", -1
}

func newVerifyResopnse(err error) *VerifyResponse {
	code := checkVerifyErrCode(err)
	msg := "成功"
	if code != 0 {
		msg = err.Error()
	}

	return &VerifyResponse{
		Code: code,
		Msg:  msg,
		Data: make([]*VerifyData, 0),
	}
}
